<!DOCTYPE html>
<html lang=zh-CN>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="google-site-verification" content="xBT4GhYoi5qRD5tr338pgPM5OWHHIDR6mNg1a3euekI" />
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta property="og:description" content="samuel-24276的博客">
    <meta property="og:type" content="website">
    <meta name="description" content="samuel-24276的博客">
    <meta name="keyword"  content="aircloud">
    <link rel="shortcut icon" href="/myblog/img/favicon.ico">

    <title>
        
        信号槽机制 - samuel-24276的博客 | samuel-24276&#39;s Blog
        
    </title>

    <!-- Custom CSS -->
    
<link rel="stylesheet" href="/myblog/css/aircloud.css">

    
<link rel="stylesheet" href="/myblog/css/gitment.css">

    <!--<link rel="stylesheet" href="https://imsun.github.io/gitment/style/default.css">-->
    <link href="//at.alicdn.com/t/font_620856_28hi1hpxx24.css" rel="stylesheet" type="text/css">
    <!-- ga & ba script hoook -->
    <script></script>
<meta name="generator" content="Hexo 7.1.1"></head>

<body>

<div class="site-nav-toggle" id="site-nav-toggle">
    <button>
        <span class="btn-bar"></span>
        <span class="btn-bar"></span>
        <span class="btn-bar"></span>
    </button>
</div>

<div class="index-about">
    <i> Like what you do, do what you like </i>
</div>

<div class="index-container">
    
    <div class="index-left">
        
<div class="nav" id="nav">
    <div class="avatar-name">
        <div class="avatar ">
            <img src="/myblog/img/avatar.jpg" />
        </div>
        <div class="name">
            <i>samuel-24276</i>
        </div>
    </div>
    <div class="contents" id="nav-content">
        <ul>
            <li >
                <a href="/myblog/">
                    <i class="iconfont icon-shouye1"></i>
                    <span>主页</span>
                </a>
            </li>
            <li >
                <a href="/myblog/tags">
                    <i class="iconfont icon-biaoqian1"></i>
                    <span>标签</span>
                </a>
            </li>
            <li >
                <a href="/myblog/archives">
                    <i class="iconfont icon-guidang2"></i>
                    <span>存档</span>
                </a>
            </li>
            <li >
                <a href="/myblog/collect/">
                    <i class="iconfont icon-shoucang1"></i>
                    <span>收藏</span>
                </a>
            </li>
            <li >
                <a href="/myblog/about/">
                    <i class="iconfont icon-guanyu2"></i>
                    <span>关于</span>
                </a>
            </li>
            
            <li>
                <a id="search">
                    <i class="iconfont icon-sousuo1"></i>
                    <span>搜索</span>
                </a>
            </li>
            
        </ul>
    </div>
    
        <div id="toc" class="toc-article">
    <ol class="toc"><li class="toc-item toc-level-1"><a class="toc-link" href="#1-Qt%E4%BF%A1%E5%8F%B7%E5%92%8C%E6%A7%BD%E4%B8%8E%E5%85%83%E5%AF%B9%E8%B1%A1%E7%B3%BB%E7%BB%9FMoc"><span class="toc-text">1.Qt信号和槽与元对象系统Moc</span></a></li><li class="toc-item toc-level-1"><a class="toc-link" href="#2-%E4%BF%A1%E5%8F%B7%E5%92%8C%E6%A7%BD%E6%9C%BA%E5%88%B6%E7%9A%84%E7%89%B9%E6%80%A7"><span class="toc-text">2.信号和槽机制的特性</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#2-1-%E5%BA%94%E7%94%A8%E5%9C%BA%E6%99%AF%EF%BC%9A"><span class="toc-text">2.1.应用场景：</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#2-2-%E4%BD%BF%E7%94%A8%E6%B3%A8%E6%84%8F"><span class="toc-text">2.2.使用注意</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#2-3-%E4%BC%98%E7%BC%BA%E7%82%B9"><span class="toc-text">2.3.优缺点</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#2-4-%E7%89%B9%E5%BE%81"><span class="toc-text">2.4.特征</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#3-Qt%E4%BF%A1%E5%8F%B7%E5%92%8C%E6%A7%BD%E7%9A%84%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F"><span class="toc-text">3.Qt信号和槽的设计模式</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#%E8%A7%82%E5%AF%9F%E8%80%85%E6%A8%A1%E5%BC%8F%E7%89%B9%E5%BE%81%EF%BC%9A"><span class="toc-text">观察者模式特征：</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#4-%E4%BF%A1%E5%8F%B7%E4%B8%8E%E6%A7%BD%E5%87%BD%E6%95%B0%E7%9A%84%E8%BF%9E%E6%8E%A5%E6%96%B9%E5%BC%8F"><span class="toc-text">4.信号与槽函数的连接方式</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#4-1-%E6%A0%87%E5%87%86%E8%BF%9E%E6%8E%A5%E6%96%B9%E5%BC%8F"><span class="toc-text">4.1.标准连接方式</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#4-1-1-%E8%AF%AD%E6%B3%95"><span class="toc-text">4.1.1.语法</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#4-1-2-%E5%8E%9F%E7%90%86"><span class="toc-text">4.1.2.原理</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#4-1-3-%E7%89%B9%E6%80%A7"><span class="toc-text">4.1.3.特性</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#4-1-4-%E8%BF%9E%E6%8E%A5%E7%B1%BB%E5%9E%8B%EF%BC%9A"><span class="toc-text">4.1.4.连接类型：</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#4-1-5-%E5%86%85%E9%83%A8%E5%AE%9E%E7%8E%B0"><span class="toc-text">4.1.5.内部实现</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#4-2-%E5%87%BD%E6%95%B0%E6%8C%87%E9%92%88%E8%BF%9E%E6%8E%A5%E6%96%B9%E5%BC%8F%EF%BC%88Qt5%E4%B9%8B%E5%90%8E%EF%BC%89"><span class="toc-text">4.2.函数指针连接方式（Qt5之后）</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#4-2-1-%E8%AF%AD%E6%B3%95"><span class="toc-text">4.2.1.语法</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#4-2-2-%E5%8E%9F%E7%90%86"><span class="toc-text">4.2.2.原理</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#4-2-3-%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9"><span class="toc-text">4.2.3.注意事项</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#4-2-4-%E7%89%B9%E6%80%A7"><span class="toc-text">4.2.4.特性</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#4-2-5-%E8%A7%A3%E5%86%B3%E6%96%B9%E5%BC%8F"><span class="toc-text">4.2.5.解决方式</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#4-2-6-%E8%BF%9E%E6%8E%A5%E7%B1%BB%E5%9E%8B"><span class="toc-text">4.2.6.连接类型</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#4-2-6-%E5%86%85%E9%83%A8%E5%AE%9E%E7%8E%B0"><span class="toc-text">4.2.6.内部实现</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#4-3-lambda%E8%A1%A8%E8%BE%BE%E5%BC%8F%EF%BC%88Qt5%E4%B9%8B%E5%90%8E%EF%BC%89"><span class="toc-text">4.3.lambda表达式（Qt5之后）</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#4-3-1-%E8%AF%AD%E6%B3%95"><span class="toc-text">4.3.1.语法</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#4-3-2-%E5%8E%9F%E7%90%86"><span class="toc-text">4.3.2.原理</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#4-3-3-%E7%89%B9%E6%80%A7"><span class="toc-text">4.3.3.特性</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#4-3-4-%E8%A7%A3%E5%86%B3%E6%96%B9%E5%BC%8F"><span class="toc-text">4.3.4.解决方式</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#4-3-5-%E8%BF%9E%E6%8E%A5%E7%B1%BB%E5%9E%8B"><span class="toc-text">4.3.5.连接类型</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#4-3-6-%E5%86%85%E9%83%A8%E5%AE%9E%E7%8E%B0"><span class="toc-text">4.3.6.内部实现</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#4-4-%E4%BA%94%E7%A7%8D%E8%BF%9E%E6%8E%A5%E7%B1%BB%E5%9E%8B"><span class="toc-text">4.4.五种连接类型</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#4-5%E8%BF%9E%E6%8E%A5%E7%9A%84%E4%B8%8D%E5%90%8C%E5%86%99%E6%B3%95-%E6%96%B0"><span class="toc-text">4.5连接的不同写法(新)</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%85%83%E6%96%B9%E6%B3%95%E5%BC%8F"><span class="toc-text">元方法式</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%87%BD%E6%95%B0%E6%8C%87%E9%92%88%E5%BC%8F"><span class="toc-text">函数指针式</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%87%BD%E6%95%B0%E9%87%8D%E8%BD%BD%E7%9A%84%E5%A4%84%E7%90%86"><span class="toc-text">函数重载的处理</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#functor%E5%BC%8F"><span class="toc-text">functor式</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%85%B3%E4%BA%8Efunctor"><span class="toc-text">关于functor</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#functor"><span class="toc-text">functor</span></a></li></ol></li></ol></li></ol>
</div>
    
</div>


<div class="search-field" id="search-field">
    <div class="search-bg" id="search-bg"></div>
    <div class="search-container">
        <div class="search-input">
            <span id="esc-search"> <i class="icon-fanhui iconfont"></i></span>
            <input id="search-input"/>
            <span id="begin-search">搜索</span>
        </div>
        <div class="search-result-container" id="search-result-container">

        </div>
    </div>
</div>

        <div class="index-about-mobile">
            <i> Like what you do, do what you like </i>
        </div>
    </div>
    
    <div class="index-middle">
        <!-- Main Content -->
        


<div class="post-container">
    <div class="post-title">
        信号槽机制
    </div>

    <div class="post-meta">
        <span class="attr">发布于：<span>2022-09-26 19:54:41</span></span>
        
        <span class="attr">标签：/
        
        <a class="tag" href="/myblog/tags/#Qt" title="Qt">Qt</a>
        <span>/</span>
        
        
        </span>
        <span class="attr">访问：<span id="busuanzi_value_page_pv"></span>
</span>
</span>
    </div>
    <div class="post-content no-indent">
        <h1 id="1-Qt信号和槽与元对象系统Moc"><a href="#1-Qt信号和槽与元对象系统Moc" class="headerlink" title="1.Qt信号和槽与元对象系统Moc"></a>1.Qt信号和槽与元对象系统Moc</h1><p>Moc元对象编译器生成对应文件（xx.h-&gt;xx.cpp-&gt;moc_xx.cpp）-&gt;GCC等编译器开始编译</p>
<blockquote>
<p>Moc编译流程</p>
</blockquote>
<ul>
<li>1.找到项目中的Q_OBJECT宏</li>
<li>2.Q_OBJECT替换类文件</li>
<li>3.添加staticMetaObject（存储当前类的相关信息）和获取</li>
<li>4.槽函数的部分调用逻辑</li>
<li>5.信号的部分调用（信号定义在moc_xx.cpp文件中，自动生成）</li>
<li>6.继承QObject的connect</li>
</ul>
<p>qt_metacall内完成了槽函数、信号的调用和类对象属性的读写和重置工作，内部调用qt_static_metacall实现槽函数的具体调用，qt_static_metacall方法的参数_id对应信号与槽的声明顺序，从0开始：</p>
<ul>
<li>1.信号在前，槽函数在后</li>
<li>2._id与访问权限无关</li>
<li>3.在信号与槽的声明顺序改变时，_id动态改变</li>
</ul>
<h1 id="2-信号和槽机制的特性"><a href="#2-信号和槽机制的特性" class="headerlink" title="2.信号和槽机制的特性"></a>2.信号和槽机制的特性</h1><p><strong>信号与槽本质上是两个对象的函数地址映射</strong>，在Qt源码中都被转换成了字符串，信号与槽函数互为一对多，多对一的关系。<br>信号：当某个类对象发生了内部变化时，发射信号随后与之关联的槽函数被执行（如同类内正常函数被调用），信号与槽机制完全独立于任何GUI循环</p>
<ul>
<li>1.信号可以连接普通函数</li>
<li>2.当一个信号对应多个槽函数时，槽函数调用顺序按照connect的顺序<br>槽函数：等同于类普通成员函数</li>
</ul>
<h2 id="2-1-应用场景："><a href="#2-1-应用场景：" class="headerlink" title="2.1.应用场景："></a>2.1.应用场景：</h2><ul>
<li>1.单线程：相当于函数指针调用</li>
<li>2.多线程：信号触发时，发送者将槽函数调用转化成一次调用事件放入事件循环队列，接收者线程执行到下一次事件处理时，处理调用事件，调用相应函数</li>
</ul>
<h2 id="2-2-使用注意"><a href="#2-2-使用注意" class="headerlink" title="2.2.使用注意"></a>2.2.使用注意</h2><ul>
<li>1.使用方式灵活，但是存在效率上的开销（损耗相对较小），不能像真正的“函数回调”一样高效</li>
<li>2.参数列表中不能使用宏定义和函数指针</li>
<li>3.参数列表中不能使用缺省值</li>
<li>4.参数列表中不能使用模板类</li>
<li>5.signal前不能设置访问权限且返回值类型只能是void，slot可以设置访问权限，返回类型也是void</li>
<li>6.signal和slot区域内不能含有构造函数、内部类（嵌套类）、友元声明（类&#x2F;函数）</li>
</ul>
<h2 id="2-3-优缺点"><a href="#2-3-优缺点" class="headerlink" title="2.3.优缺点"></a>2.3.优缺点</h2><p>1.优点：</p>
<ul>
<li>1.类型安全，相关联的信号和槽的签名等同（signal和slot的参数类型和参数个数相同）</li>
<li>2.松耦合，削弱了Qt对象之间的耦合度（两个对象都互相不知道对方的存在，仍然可以建立联系，关联运行时删除，程序不崩溃）<br>2.缺点：效率低，信号触发到槽函数调用比直接调用非虚函数慢10倍</li>
</ul>
<h2 id="2-4-特征"><a href="#2-4-特征" class="headerlink" title="2.4.特征"></a>2.4.特征</h2><ul>
<li>1.信号与槽是观察者模式的一种实现（订阅发布）</li>
<li>2.一个信号就是一个能被观察的事件，或者至少是事件已经发生的通知</li>
<li>3.一个槽就是一个观察者，通常就是在被观察对象发生变化的时候，也可以说是信号发出的时候——被调用的函数</li>
<li>4.信号与槽函数形成一种被观察者——多观察者模式</li>
<li>5.当事件或状态改变时，信号发出，同时信号发出者有义务调用所有注册的、关联此信号的槽函数</li>
</ul>
<h1 id="3-Qt信号和槽的设计模式"><a href="#3-Qt信号和槽的设计模式" class="headerlink" title="3.Qt信号和槽的设计模式"></a>3.Qt信号和槽的设计模式</h1><p>Qt的信号与槽系统采用了观察者模式：目标subject（被观察者）和观察者observer，信号对应被观察者，槽函数对应观察者。<br>观察者模式定义了对象间的一种一对多（变化）依赖关系，以便<strong>当一个对象（subject）状态发生变化时，所有依赖的对象（observer）都得到通知</strong>。——单个信号可以对应多个槽函数，单个槽函数也可以对应多个信号。</p>
<h2 id="观察者模式特征："><a href="#观察者模式特征：" class="headerlink" title="观察者模式特征："></a>观察者模式特征：</h2><ul>
<li>1.使用面向对象的抽象，<strong>观察者模式使得我们可以独立地改变目标与观察者，达到松耦合</strong></li>
<li>2.目标发送通知时，无需指定观察者通知（可携带通知信息作为参数）</li>
<li>3.观察者自己决定是否订阅通知，目标对象一无所知（目标对象对观察者进行注册register和通知notify）——Qt的connect方法进行信号注册和绑定</li>
</ul>
<h1 id="4-信号与槽函数的连接方式"><a href="#4-信号与槽函数的连接方式" class="headerlink" title="4.信号与槽函数的连接方式"></a>4.信号与槽函数的连接方式</h1><h2 id="4-1-标准连接方式"><a href="#4-1-标准连接方式" class="headerlink" title="4.1.标准连接方式"></a>4.1.标准连接方式</h2><h3 id="4-1-1-语法"><a href="#4-1-1-语法" class="headerlink" title="4.1.1.语法"></a>4.1.1.语法</h3><figure class="highlight oxygene"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">QObject::connect(<span class="keyword">const</span> QObject *sender, <span class="keyword">const</span> char *signal, <span class="keyword">const</span> QObject *receiver, <span class="keyword">const</span> char *<span class="keyword">method</span>, <span class="title function_">Qt</span>::ConnectionType <span class="keyword">type</span> = Qt::AutoConnection)</span><br></pre></td></tr></table></figure>

<h3 id="4-1-2-原理"><a href="#4-1-2-原理" class="headerlink" title="4.1.2.原理"></a>4.1.2.原理</h3><p>将信号槽的函数名称按规则组合成字符串，通过connect解析该字符串，分别获取信号槽的绝对索引，然后将信号槽连接添加到信号容器中</p>
<h3 id="4-1-3-特性"><a href="#4-1-3-特性" class="headerlink" title="4.1.3.特性"></a>4.1.3.特性</h3><ul>
<li>1.不支持调用普通成员函数；</li>
<li>2.支持信号与槽函数的重载。</li>
</ul>
<h3 id="4-1-4-连接类型："><a href="#4-1-4-连接类型：" class="headerlink" title="4.1.4.连接类型："></a>4.1.4.连接类型：</h3><p>默认：Qt::AutoConnection<br>设置：可手动设置</p>
<h3 id="4-1-5-内部实现"><a href="#4-1-5-内部实现" class="headerlink" title="4.1.5.内部实现"></a>4.1.5.内部实现</h3><p><code>QObject::connect</code></p>
<h2 id="4-2-函数指针连接方式（Qt5之后）"><a href="#4-2-函数指针连接方式（Qt5之后）" class="headerlink" title="4.2.函数指针连接方式（Qt5之后）"></a>4.2.函数指针连接方式（Qt5之后）</h2><h3 id="4-2-1-语法"><a href="#4-2-1-语法" class="headerlink" title="4.2.1.语法"></a>4.2.1.语法</h3><figure class="highlight oxygene"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">QObject::connect(<span class="keyword">const</span> QObject *sender, <span class="keyword">const</span> QMetaMethod &amp;signal, <span class="keyword">const</span> QObject *receiver, <span class="keyword">const</span> QMetaMethod &amp;<span class="keyword">method</span>, <span class="title function_">Qt</span>::ConnectionType <span class="keyword">type</span> = Qt::AutoConnection)</span><br></pre></td></tr></table></figure>

<h3 id="4-2-2-原理"><a href="#4-2-2-原理" class="headerlink" title="4.2.2.原理"></a>4.2.2.原理</h3><p>编译时检查，使用函数指针作为信号与槽函数</p>
<h3 id="4-2-3-注意事项"><a href="#4-2-3-注意事项" class="headerlink" title="4.2.3.注意事项"></a>4.2.3.注意事项</h3><p>若拼写错误无法通过编译</p>
<h3 id="4-2-4-特性"><a href="#4-2-4-特性" class="headerlink" title="4.2.4.特性"></a>4.2.4.特性</h3><ul>
<li>1.调用逻辑层次分明，在编译时检查避免程序运行时错误</li>
<li>2.支持调用普通成员函数和槽函数</li>
<li>3.不能定义两个重载信号函数，否则无法识别（因为没有指定形参）。</li>
</ul>
<h3 id="4-2-5-解决方式"><a href="#4-2-5-解决方式" class="headerlink" title="4.2.5.解决方式"></a>4.2.5.解决方式</h3><ul>
<li>1.<code>static_cast&lt;void (类名::*)(参数类型列表)&gt;(信号函数地址)</code></li>
<li>2.<code>QOverload&lt;形参类型列表&gt;::of(信号函数地址)</code></li>
</ul>
<h3 id="4-2-6-连接类型"><a href="#4-2-6-连接类型" class="headerlink" title="4.2.6.连接类型"></a>4.2.6.连接类型</h3><p>默认：Qt::AutoConnection<br>设置：可手动设置</p>
<h3 id="4-2-6-内部实现"><a href="#4-2-6-内部实现" class="headerlink" title="4.2.6.内部实现"></a>4.2.6.内部实现</h3><ul>
<li>1.<code>template&lt;typename func1, typename func2&gt;</code></li>
<li>2.<code>static inline QMetaObject::Connection connect</code></li>
</ul>
<h2 id="4-3-lambda表达式（Qt5之后）"><a href="#4-3-lambda表达式（Qt5之后）" class="headerlink" title="4.3.lambda表达式（Qt5之后）"></a>4.3.lambda表达式（Qt5之后）</h2><h3 id="4-3-1-语法"><a href="#4-3-1-语法" class="headerlink" title="4.3.1.语法"></a>4.3.1.语法</h3><figure class="highlight qml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">QObject</span>::connect(<span class="keyword">const</span> QObject *sender, <span class="keyword">const</span> QMetaMethod &amp;<span class="keyword">signal</span><span class="string"></span>, [=](形参列表)&#123;</span><br><span class="line">    函数体</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>

<h3 id="4-3-2-原理"><a href="#4-3-2-原理" class="headerlink" title="4.3.2.原理"></a>4.3.2.原理</h3><p>编译时检查，使用函数指针作为信号与槽函数，编译时如果信号槽出现拼写错误无法通过编译，表达式返回的是槽函数的函数指针</p>
<h3 id="4-3-3-特性"><a href="#4-3-3-特性" class="headerlink" title="4.3.3.特性"></a>4.3.3.特性</h3><ul>
<li>1.支持槽函数重载</li>
<li>2.不支持信号重载</li>
</ul>
<h3 id="4-3-4-解决方式"><a href="#4-3-4-解决方式" class="headerlink" title="4.3.4.解决方式"></a>4.3.4.解决方式</h3><ul>
<li>1.<code>static_cast&lt;void (类名::*)(参数类型列表)&gt;(信号函数地址)</code></li>
<li>2.<code>QOverload&lt;形参类型列表&gt;::of(信号函数地址)</code></li>
</ul>
<h3 id="4-3-5-连接类型"><a href="#4-3-5-连接类型" class="headerlink" title="4.3.5.连接类型"></a>4.3.5.连接类型</h3><p>默认：Qt::DirectConnection<br>设置：<strong>不可手动设置</strong></p>
<h3 id="4-3-6-内部实现"><a href="#4-3-6-内部实现" class="headerlink" title="4.3.6.内部实现"></a>4.3.6.内部实现</h3><ul>
<li>1.<code>template&lt;typename func1, typename func2&gt;</code></li>
<li>2.<code>static inline typename std::enable_if&lt;QtPrivate::FunctionPointer&lt;Func2&gt;::ArgumentCount=-1, QMetaObject::Connection &gt;::type</code></li>
<li>3.<code>static inline QMetaObject::Connection connect</code></li>
</ul>
<h2 id="4-4-五种连接类型"><a href="#4-4-五种连接类型" class="headerlink" title="4.4.五种连接类型"></a>4.4.五种连接类型</h2><ul>
<li>1.Qt::AutoConnection（默认值）<br>如果信号在接收者依附的线程内发射，等同于直接连接；否则等同于队列连接</li>
<li>Qt::DirectConnection<br>信号发射时，槽函数被直接调用，不论槽函数所属对象位于哪个线程，槽函数都在发射信号的线程内执行（不能跨线程）</li>
<li>Qt::QueuedConnection<br>当控制权回到接收者依附线程的事件循环时，槽函数被调用。槽函数在接收者线程内执行（可在线程内也可跨线程传递消息）</li>
<li>Qt::BlockingQueuedConnection<br>与队列连接方式类似，但是发射信号后阻塞，直到关联槽函数执行后（信号和槽不能在同一线程内，否则死锁）</li>
<li>Qt::UniqueConnection<br>防止重复连接，若信号与槽已经连接过了，则不会再连接</li>
</ul>
<blockquote>
<p>知识补充<br>基本概念（vtabl &#x3D; 虚函数表， vptr &#x3D; 虚表指针，指向虚表的指针）</p>
</blockquote>
<h2 id="4-5连接的不同写法-新"><a href="#4-5连接的不同写法-新" class="headerlink" title="4.5连接的不同写法(新)"></a>4.5连接的不同写法(新)</h2><p>connect函数重载实现了多种不同的参数写法，以Qt5.12为例，大致分为三类:</p>
<p>元方法式、函数指针式、functor式</p>
<h3 id="元方法式"><a href="#元方法式" class="headerlink" title="元方法式"></a>元方法式</h3><p>元方法式是最常用的写法，函数声明如下：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//connect(1) 字符串式信号槽</span></span><br><span class="line"><span class="function"><span class="type">static</span> QMetaObject::Connection <span class="title">connect</span><span class="params">(<span class="type">const</span> QObject *sender, <span class="type">const</span> <span class="type">char</span> *signal,</span></span></span><br><span class="line"><span class="params"><span class="function">                   <span class="type">const</span> QObject *receiver, <span class="type">const</span> <span class="type">char</span> *member, Qt::ConnectionType = Qt::AutoConnection)</span></span>;</span><br><span class="line"><span class="comment">//connect(2) QMetaMethod式信号槽</span></span><br><span class="line"><span class="function"><span class="type">static</span> QMetaObject::Connection <span class="title">connect</span><span class="params">(<span class="type">const</span> QObject *sender, <span class="type">const</span> QMetaMethod &amp;signal,</span></span></span><br><span class="line"><span class="params"><span class="function">                   <span class="type">const</span> QObject *receiver, <span class="type">const</span> QMetaMethod &amp;method,</span></span></span><br><span class="line"><span class="params"><span class="function">                   Qt::ConnectionType type = Qt::AutoConnection)</span></span>;</span><br><span class="line"><span class="comment">//connect(3) 对(1)的重载, 非static去掉receiver</span></span><br><span class="line"><span class="function"><span class="keyword">inline</span> QMetaObject::Connection <span class="title">connect</span><span class="params">(<span class="type">const</span> QObject *sender, <span class="type">const</span> <span class="type">char</span> *signal,</span></span></span><br><span class="line"><span class="params"><span class="function">                       <span class="type">const</span> <span class="type">char</span> *member, Qt::ConnectionType type = Qt::AutoConnection)</span> <span class="type">const</span></span>;</span><br></pre></td></tr></table></figure>

<p>Qt应用程序中用到最多的是connect(1)的写法，例如:</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">Tom tom;</span><br><span class="line">Jerry jerry;</span><br><span class="line"><span class="built_in">connect</span>(&amp;tom, <span class="built_in">SIGNAL</span>(<span class="built_in">miao</span>()), &amp;jerry, <span class="built_in">SLOT</span>(<span class="built_in">runAway</span>()))</span><br></pre></td></tr></table></figure>

<p>其中SIGNAL、SLOT两个宏, 作用是将函数转换成字符串。</p>
<p>connect(1)的实现是靠字符串去查找元方法，以实现连接。</p>
<p>connect(2) 则是把信号槽的字符串换成了元方法QMetaMethod, 一般不会直接用这种写法。</p>
<p>connect(3)是对connect(1)的重载，非静态成员函数，本身有this指针，所以省略了receiver参数。</p>
<h3 id="函数指针式"><a href="#函数指针式" class="headerlink" title="函数指针式"></a>函数指针式</h3><p>函数指针式写法，声明如下：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//connect(4) 连接信号到qobject的成员函数</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> Func1, <span class="keyword">typename</span> Func2&gt;</span><br><span class="line">   <span class="function"><span class="type">static</span> <span class="keyword">inline</span> QMetaObject::Connection <span class="title">connect</span><span class="params">(<span class="type">const</span> <span class="keyword">typename</span> QtPrivate::FunctionPointer&lt;Func1&gt;::Object *sender, Func1 signal,</span></span></span><br><span class="line"><span class="params"><span class="function">                                    <span class="type">const</span> <span class="keyword">typename</span> QtPrivate::FunctionPointer&lt;Func2&gt;::Object *receiver, Func2 slot,</span></span></span><br><span class="line"><span class="params"><span class="function">                                    Qt::ConnectionType type = Qt::AutoConnection)</span></span>;</span><br><span class="line">								 </span><br><span class="line"><span class="comment">//connect(5) 连接信号到非成员函数。</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> Func1, <span class="keyword">typename</span> Func2&gt;</span><br><span class="line">   <span class="type">static</span> <span class="keyword">inline</span> <span class="keyword">typename</span> std::enable_if&lt;<span class="built_in">int</span>(QtPrivate::FunctionPointer&lt;Func2&gt;::ArgumentCount) &gt;= <span class="number">0</span>, QMetaObject::Connection&gt;::type</span><br><span class="line">           <span class="built_in">connect</span>(<span class="type">const</span> <span class="keyword">typename</span> QtPrivate::FunctionPointer&lt;Func1&gt;::Object *sender, Func1 signal, Func2 slot);</span><br><span class="line">   </span><br><span class="line"><span class="comment">//connect(6) 连接信号到非成员函数。比(5)多一个context,可以设置连接类型</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> Func1, <span class="keyword">typename</span> Func2&gt;</span><br><span class="line">   <span class="type">static</span> <span class="keyword">inline</span> <span class="keyword">typename</span> std::enable_if&lt;<span class="built_in">int</span>(QtPrivate::FunctionPointer&lt;Func2&gt;::ArgumentCount) &gt;= <span class="number">0</span> &amp;&amp;</span><br><span class="line">                                         !QtPrivate::FunctionPointer&lt;Func2&gt;::IsPointerToMemberFunction, QMetaObject::Connection&gt;::type</span><br><span class="line">           <span class="built_in">connect</span>(<span class="type">const</span> <span class="keyword">typename</span> QtPrivate::FunctionPointer&lt;Func1&gt;::Object *sender, Func1 signal, <span class="type">const</span> QObject *context, Func2 slot,</span><br><span class="line">                   Qt::ConnectionType type = Qt::AutoConnection);</span><br></pre></td></tr></table></figure>

<p>connect(4)用的也比较多，用法如下:</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">Tom tom;</span><br><span class="line">Jerry jerry;</span><br><span class="line"><span class="built_in">connect</span>(&amp;tom, &amp;Tom::miao, &amp;jerry, &amp;Jerry::runAway);</span><br></pre></td></tr></table></figure>

<p>信号-槽换成了C++的 取成员函数指针 的形式。</p>
<p>connect(4)本身的实现，比connect(1)快一些，因为省去了字符串查找的过程。</p>
<p>而连接建立后，从信号触发到槽函数的执行，两种写法是没有区别的。</p>
<p>**在一些需要”运行期反射”的情况下(头文件都没有,只知道有这么个对象,和函数的名字),只能用connect(1)**。</p>
<p>connect(5)可以连接信号到任意非成员函数指针上。除了槽函数，普通的函数也可以连接。这种连接不支持设置连接类型，可以看作是单纯的函数调用。</p>
<p>connect(6)是对connect(5)的重载,增加了一个context对象代替reveicer对象的作用。这种连接是可以设置连接类型的。</p>
<h3 id="函数重载的处理"><a href="#函数重载的处理" class="headerlink" title="函数重载的处理"></a>函数重载的处理</h3><p>信号-槽函数有重载的情况下，写函数指针式connect会报错，就需要类型转换。</p>
<p>比如：QLocalSocket有一个成员函数error,也有一个信号error,直接写connect会报错的。</p>
<p>Qt为我们提供了QOverload这个模板类，以解决这个问题。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//连接重载过的函数，使用QOverload做类型转换</span></span><br><span class="line"><span class="built_in">connect</span>(socket, QOverload&lt;QLocalSocket::LocalSocketError&gt;::<span class="built_in">of</span>(&amp;QLocalSocket::error), <span class="keyword">this</span>, &amp;XXX::onError);</span><br></pre></td></tr></table></figure>

<p>如果编译器支持C++14，还可以用qOverload模板函数</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//连接重载过的函数，使用qOverload做类型转换</span></span><br><span class="line"><span class="built_in">connect</span>(socket, <span class="built_in">qOverload</span>&lt;QLocalSocket::LocalSocketError&gt;(&amp;QLocalSocket::error), <span class="keyword">this</span>, &amp;XXX::onError);</span><br></pre></td></tr></table></figure>

<p>还有像QNetworkReply::error、QProcess::finished等等，都有重载，用的时候要转换处理一下。</p>
<h3 id="functor式"><a href="#functor式" class="headerlink" title="functor式"></a>functor式</h3><h3 id="关于functor"><a href="#关于functor" class="headerlink" title="关于functor"></a>关于functor</h3><p>问: 什么是functor？functor有什么用?</p>
<p>答: 在C++11之前, Qt通过自己的实现来推导函数指针及其参数，即QtPrivate::FunctionPointer, 用来处理信号-槽的连接。</p>
<p>C++11带来了lambda, 以及std::bind和std::function, std::function本身可以存储lambda、std::bind以及FunctionPointer。</p>
<p>这时候Qt已有的connect(4)、connect(5)、connect(6)是可以支持FunctionPointer的,而新出现的lambda以及std::bind是不支持的，</p>
<p>QtPrivate::FunctionPointer推导不出这些类型。所以Qt把这些不支持的新类型(主要是lambda和std::bind)称为functor(文档和源码都这么命名)，</p>
<p>并增加了connect(7)和connect(8)以支持functor。</p>
<h3 id="functor"><a href="#functor" class="headerlink" title="functor"></a>functor</h3><p>functor式写法，声明如下：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//connect(7) 连接信号到任意functor</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> Func1, <span class="keyword">typename</span> Func2&gt;</span><br><span class="line"><span class="type">static</span> <span class="keyword">inline</span> <span class="keyword">typename</span> std::enable_if&lt;QtPrivate::FunctionPointer&lt;Func2&gt;::ArgumentCount == <span class="number">-1</span>, QMetaObject::Connection&gt;::type</span><br><span class="line">       <span class="built_in">connect</span>(<span class="type">const</span> <span class="keyword">typename</span> QtPrivate::FunctionPointer&lt;Func1&gt;::Object *sender, Func1 signal, Func2 slot);</span><br><span class="line">		</span><br><span class="line"><span class="comment">//connect(8) 连接信号到任意functor。比(7)多一个context,可以设置连接类型</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> Func1, <span class="keyword">typename</span> Func2&gt;</span><br><span class="line">   <span class="type">static</span> <span class="keyword">inline</span> <span class="keyword">typename</span> std::enable_if&lt;QtPrivate::FunctionPointer&lt;Func2&gt;::ArgumentCount == <span class="number">-1</span>, QMetaObject::Connection&gt;::type</span><br><span class="line">           <span class="built_in">connect</span>(<span class="type">const</span> <span class="keyword">typename</span> QtPrivate::FunctionPointer&lt;Func1&gt;::Object *sender, Func1 signal, <span class="type">const</span> QObject *context, Func2 slot,</span><br><span class="line">                   Qt::ConnectionType type = Qt::AutoConnection);</span><br></pre></td></tr></table></figure>

<p>connect(7)可以连接信号到任意lambda、std::bind上。</p>
<p>connect(8)是对(7)的重载，增加了一个context对象代替reveicer对象的作用。这种连接是可以设置连接类型的。</p>
<hr>

        
            <div class="donate-container">
    <div class="donate-button">
        <button id="donate-button">赞赏</button>
    </div>
    <div class="donate-img-container hide" id="donate-img-container">
        <img id="donate-img" src="" data-src="/myblog/img/donate.jpg">
        <p> 感谢鼓励 </p>
    </div>
</div>
        
        <br />
        <div id="comment-container">
        </div>
        <div id="disqus_thread"></div>
        <div id="lv-container"></div>
        <div class="giscus"></div>
    </div>
</div>

    </div>
</div>


<footer class="footer">
    <ul class="list-inline text-center">
        
        

        

        

        
        <li>
            <a target="_blank"  href="https://github.com/samuel-24276">
                            <span class="fa-stack fa-lg">
                                <i class="iconfont icon-github"></i>
                            </span>
            </a>
        </li>
        

        

    </ul>
    
    <p>
        <span>/</span>
        
        <span><a target="_blank" rel="noopener" href="https://github.com/samuel-24276">samuel-24276&#39;s Page</a></span>
        <span>/</span>
        
        <span><a target="_blank" rel="noopener" href="https://beian.miit.gov.cn/">北京-未备案</a></span>
        <span>/</span>
        
        <span><a href="#">返回顶部</a></span>
        <span>/</span>
        
    </p>
    
    <p>
        <span id="busuanzi_container_site_pv">
            <span id="busuanzi_value_site_pv"></span>PV
        </span>
        <span id="busuanzi_container_site_uv">
            <span id="busuanzi_value_site_uv"></span>UV
        </span>
        Created By <a target="_blank" rel="noopener" href="https://hexo.io/">Hexo</a>  Theme <a target="_blank" rel="noopener" href="https://github.com/aircloud/hexo-theme-aircloud">AirCloud</a></p>
</footer>




</body>

<script>
    // We expose some of the variables needed by the front end
    window.hexo_search_path = "search.json"
    window.hexo_root = "/myblog/"
    window.isPost = true
</script>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>

<script src="/myblog/js/index.js"></script>

<script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>






    <script type="text/javascript">
       (function() {
           if (typeof LivereTower === 'function') { return; }

           var j, d = document.getElementById('lv-container');

           d.setAttribute('data-id','city');
           d.setAttribute('data-uid' , 'MTAyMC80OTA5OC8yNTU5Mw==');

           j = document.createElement('script');
           j.src = 'https://cdn-city.livere.com/js/embed.dist.js';
           j.async = true;

           d.appendChild(j);
       })();
    </script>
    <noscript>为正常使用来必力评论功能请激活JavaScript</noscript>
    </div>

</html>
